/*
Copyright (c) 2009-2010, Warwick Warp Limited
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Warwick Warp Limited nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL WARWICK WARP LIMITED BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef WOPTIONS_OPTION_BINDERS_H_INCLUDED
#define WOPTIONS_OPTION_BINDERS_H_INCLUDED

#include <sstream>
#include <vector>

#include "detail/boost_headers.h"
#include "detail/convertorthrow.h"
#include "argumentexception.h"

namespace woptions { 
	
namespace basic {

/*!
	\brief Base class for classes that respond to command line option
	
	Derived classes must be copy-constructable.
	
	\ingroup goptbind
*/
template<typename char_t>
class bind_option
{
public:
	//! String type
	typedef std::basic_string<char_t> string_type;
	
	virtual ~bind_option() {}
	
	/*!
		\brief Handle a command line option, previously registered with option. 
		\throws ArgumentException if an error occurs
	*/
	virtual void operator()(
		const string_type& opt,              //!< The option that was passed
		const std::vector<string_type>& args //!< The arguments for the option
		) = 0;
	
	/*!
		\brief Get the default value of an option.
	*/
	virtual std::string default_string() const { return std::string(); }
	
	/*!
		\brief Get the number of arguments an option takes
	*/
	virtual size_t num_args() const { return 0; }
};

/*! 
	\brief Ignore an option

	\ingroup goptbind
*/
template<typename char_t>
class bind_option_ignore : public bind_option<char_t>
{
public:
	//! String type
	typedef typename bind_option<char_t>::string_type string_type;
	
	//! Create a binder that simply discards an option and its arguments
	explicit bind_option_ignore(
		size_t args=0 //!< Number of additional arguments to ignore
		) : m_args(args) {}
	
	void operator()(const string_type& opt,const std::vector<string_type>& args) 
	{
	}
	
	size_t num_args() const 
	{
		return m_args;
	}

private:
	size_t m_args;
};

} // namespace basic

namespace detail {

//! Form an error string when 'opt' is invalid
template<typename char_t>
inline std::string invalid_option(const std::basic_string<char_t>& opt)
{
	std::string opt_s(opt.begin(),opt.end());
	return std::string("Invalid argument for option ")+opt_s;
}

//! Set a variable according to a command line option
template<typename char_t, typename TYPE>
class bind_option_var_type1 : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Set a variable from an option
	explicit bind_option_var_type1(
		TYPE& var //!< Variable to set
		) : 
		m_var(&var),
		m_default(detail::to_string(var)) {}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		assert(args.size() == 1);
		*m_var = detail::convert_or_throw<TYPE>(
			args[0],
			detail::invalid_option(opt));
	}
	
	std::string default_string() const { return m_default; }
	size_t num_args() const { return 1; }
	
private:
	TYPE *m_var;
	std::string m_default;
};

//! Set two variables according to a command line option
template<typename char_t, typename TYPE1, typename TYPE2>
class bind_option_var_type2 : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Set variables from an option
	explicit bind_option_var_type2(
		TYPE1& var1, //!< Variable to set based on the first argument
		TYPE2& var2  //!< Variable to set based on the second argument
		) : 
		m_var1(&var1),
		m_var2(&var2)
	{
		std::string d1 = detail::to_string(var1);
		std::string d2 = detail::to_string(var2);
		if(!d1.empty() || !d2.empty())
			m_default = d1+","+d2;
	}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		assert(args.size() == 2);
		*m_var1 = detail::convert_or_throw<TYPE1>(
			args[0],
			detail::invalid_option(opt));
		*m_var2 = detail::convert_or_throw<TYPE2>(
			args[1],
			detail::invalid_option(opt));
	}
	
	std::string default_string() const { return m_default; }
	size_t num_args() const { return 2; }
	
private:
	TYPE1 *m_var1;
	TYPE2 *m_var2;
	std::string m_default;
};

//! Set three variables according to a command line option
template<typename char_t, typename TYPE1, typename TYPE2, typename TYPE3>
class bind_option_var_type3 : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Set variables from an option
	explicit bind_option_var_type3(
		TYPE1& var1, //!< Variable to bind to the first argument
		TYPE2& var2, //!< Variable to bind to the second argument
		TYPE3& var3  //!< Variable to bind to the third argument
		) : 
		m_var1(&var1),
		m_var2(&var2),
		m_var3(&var3)
	{
		std::string d1 = detail::to_string(var1);
		std::string d2 = detail::to_string(var2);
		std::string d3 = detail::to_string(var3);
		if(!d1.empty() || !d2.empty() || !d3.empty())
			m_default = d1+","+d2+","+d3;
	}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		assert(args.size() == 3);
		*m_var1 = detail::convert_or_throw<TYPE1>(
			args[0],
			detail::invalid_option(opt));
		*m_var2 = detail::convert_or_throw<TYPE2>(
			args[1],
			detail::invalid_option(opt));
		*m_var3 = detail::convert_or_throw<TYPE3>(
			args[2],
			detail::invalid_option(opt));
	}
	
	std::string default_string() const { return m_default; }
	size_t num_args() const { return 3; }
	
private:
	TYPE1 *m_var1;
	TYPE2 *m_var2;
	TYPE3 *m_var3;
	std::string m_default;
};

//! Type for setting a variable to a particular value when an option is seen
template<typename char_t, class TYPE>
class bind_option_value_type : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Set a variable to a value when an option is seen
	bind_option_value_type(
		TYPE& var,      //!< Variable to set
		const TYPE& val //!< Value to set the variable to
		) : 
		m_var(&var), 
		m_val(val) ,
		m_default(var == val)
	{
	}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		*m_var = m_val;
	}
	
	std::string default_string() const 
	{
		return m_default?"default":"";
	}
	
	size_t num_args() const { return 0; }
	
private:
	TYPE *m_var;
	TYPE m_val;
	bool m_default;
};

//! Type for calling a callback when an option is seen
template<typename char_t>
class bind_option_callback_type : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Callback type
	typedef boost::function2<
		void,
		const string_type&,
		const std::vector<string_type>&
		> function_type;
	//! Callback binder
	bind_option_callback_type(
		function_type func, //! Callback function (or function object)
		size_t args         //! Number of arguments to the option
		) : 
		m_func(func), 
		m_args(args)
	{
	}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		m_func(opt,args);
	}
	
	size_t num_args() const { return m_args; }
	
private:
	function_type m_func;
	size_t m_args;
};

//! Bind an option to a function with no arguments
template<typename char_t, typename RT>
class bind_option_fun_type0 : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Function type
	typedef boost::function0<RT> function_type;
	//! Bind an option to a function with no arguments
	explicit bind_option_fun_type0(const function_type& func) : m_func(func) {}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		assert(args.size() == 0);
		m_func();
	}
	
	size_t num_args() const { return 0; }

private:
	function_type m_func;
};

//! Bind an option to a function with 1 argument
template<typename char_t, typename RT, typename T1>
class bind_option_fun_type1 : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Function type
	typedef boost::function1<RT,T1> function_type;
	//! Bind an option to a function with 1 argument
	explicit bind_option_fun_type1(const function_type& func) : m_func(func) {}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		assert(args.size() == 1);
		std::string invalid = detail::invalid_option(opt);
		m_func(
			detail::convert_or_throw<typename detail::remove_quals<T1>::type>(args[0],invalid)
			);
	}
	
	size_t num_args() const { return 1; }

private:
	function_type m_func;
};

//! Bind an option to a function with 2 arguments
template<typename char_t, typename RT, typename T1, typename T2>
class bind_option_fun_type2 : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Function type
	typedef boost::function2<RT,T1,T2> function_type;
	//! Bind an option to a function with 2 arguments
	explicit bind_option_fun_type2(const function_type& func) : m_func(func) {}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		assert(args.size() == 2);
		std::string invalid = detail::invalid_option(opt);
		m_func(
			detail::convert_or_throw<typename detail::remove_quals<T1>::type>(args[0],invalid),
			detail::convert_or_throw<typename detail::remove_quals<T2>::type>(args[1],invalid)
			);
	}
	
	size_t num_args() const { return 2; }

private:
	function_type m_func;
};

//! Bind an option to a function with 3 arguments
template<typename char_t, typename RT, typename T1, typename T2, typename T3>
class bind_option_fun_type3 : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Function type
	typedef boost::function3<RT,T1,T2,T3> function_type;
	//! Bind an option to a function with 2 arguments
	explicit bind_option_fun_type3(const function_type& func) : m_func(func) {}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		assert(args.size() == 3);
		std::string invalid = detail::invalid_option(opt);
		m_func(
			detail::convert_or_throw<typename detail::remove_quals<T1>::type>(args[0],invalid),
			detail::convert_or_throw<typename detail::remove_quals<T2>::type>(args[1],invalid),
			detail::convert_or_throw<typename detail::remove_quals<T3>::type>(args[2],invalid)
			);
	}
	
	size_t num_args() const { return 3; }

private:
	function_type m_func;
};

//! \brief Generic function binder for resolving function types
template<typename char_t,typename F,size_t N>
struct function_binder_n
{
};

//! \brief Function binder for functions taking no arguments
template<typename char_t,typename F>
struct function_binder_n<char_t,F,0>
{
	//! Return type
	typedef typename boost::function_types::result_type<F>::type RT;
	//! Binder type
	typedef bind_option_fun_type0<char_t,RT> type;
};

//! \brief Function binder for functions taking one argument
template<typename char_t,typename F>
struct function_binder_n<char_t,F,1>
{
	//! Return type
	typedef typename boost::function_types::result_type<F>::type RT;
	//! Parameter list
	typedef typename boost::function_types::parameter_types<F> PT;
	//! Arg type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<0> >::type T1;
	//! Binder type
	typedef bind_option_fun_type1<char_t,RT,T1> type;
};

//! \brief Function binder for functions taking two arguments
template<typename char_t,typename F>
struct function_binder_n<char_t,F,2>
{
	//! Return type
	typedef typename boost::function_types::result_type<F>::type RT;
	//! Parameter list
	typedef typename boost::function_types::parameter_types<F> PT;
	//! Arg 1 type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<0> >::type T1;
	//! Arg 2 type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<1> >::type T2;
	//! Binder type
	typedef bind_option_fun_type2<char_t,RT,T1,T2> type;
};

//! \brief Function binder for functions taking three arguments
template<typename char_t,typename F>
struct function_binder_n<char_t,F,3>
{
	//! Return type
	typedef typename boost::function_types::result_type<F>::type RT;
	//! Parameter list
	typedef typename boost::function_types::parameter_types<F> PT;
	//! Arg 1 type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<0> >::type T1;
	//! Arg 2 type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<1> >::type T2;
	//! Arg 3 type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<2> >::type T3;
	//! Binder type
	typedef bind_option_fun_type3<char_t,RT,T1,T2,T3> type;
};

//! \brief Wrapper for function_binder_n that resolves the number of arguments
template<typename char_t,typename F>
struct function_binder
{
	//! \brief Get the function_binder_n that corresponds to F
	typedef typename function_binder_n<
		char_t,
		F,
		boost::function_types::function_arity<F>::value
		>::type type;
};

//! Generic function binder for member functions
template<typename char_t,typename F,size_t N>
struct mem_function_binder_n
{
};

//! Function binder for member functions with no arguments
template<typename char_t,typename F>
struct mem_function_binder_n<char_t,F,1>
{
	//! Return type
	typedef typename boost::function_types::result_type<F>::type RT;
	//! Parameter list
	typedef typename boost::function_types::parameter_types<F> PT;
	//! Object type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<0> >::type this_type;
	//! Binder type
	typedef bind_option_fun_type0<char_t,RT> type;
	//! Create a binder that calls func on c
	template<typename CLS>
	static type bind(CLS *c, F func)
	{
		return type(boost::bind(func,c));
	}
};

//! Function binder for member functions with 1 argument
template<typename char_t,typename F>
struct mem_function_binder_n<char_t,F,2>
{
	//! Return type
	typedef typename boost::function_types::result_type<F>::type RT;
	//! Parameter list
	typedef typename boost::function_types::parameter_types<F> PT;
	//! Argument type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<1> >::type T1;
	//! Object type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<0> >::type this_type;
	//! Binder type
	typedef bind_option_fun_type1<char_t,RT,T1> type;
	//! Create a binder that calls func on c
	template<typename CLS>
	static type bind(CLS *c, F func)
	{
		return type(boost::bind<RT>(func,c,_1));
	}
};

//! Function binder for member functions with 2 arguments
template<typename char_t,typename F>
struct mem_function_binder_n<char_t,F,3>
{
	//! Return type
	typedef typename boost::function_types::result_type<F>::type RT;
	//! Parameter list
	typedef typename boost::function_types::parameter_types<F> PT;
	//! Arg 1 type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<1> >::type T1;
	//! Arg 2 type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<2> >::type T2;
	//! Object type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<0> >::type this_type;
	//! Binder type
	typedef bind_option_fun_type2<char_t,RT,T1,T2> type;
	//! Create a binder that calls func on c
	template<typename CLS>
	static type bind(CLS *c, F func)
	{
		return type(boost::bind(func,c,_1,_2));
	}
};

//! Function binder for member functions with 3 arguments
template<typename char_t,typename F>
struct mem_function_binder_n<char_t,F,4>
{
	//! Return type
	typedef typename boost::function_types::result_type<F>::type RT;
	//! Parameter list
	typedef typename boost::function_types::parameter_types<F> PT;
	//! Arg 1 type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<1> >::type T1;
	//! Arg 2 type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<2> >::type T2;
	//! Arg 3 type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<3> >::type T3;
	//! Object type
	typedef typename boost::mpl::at<PT, boost::mpl::int_<0> >::type this_type;
	//! Binder type
	typedef bind_option_fun_type3<char_t,RT,T1,T2,T3> type;
	//! Create a binder that calls func on c
	template<typename CLS>
	static type bind(CLS *c, F func)
	{
		return type(boost::bind(func,c,_1,_2,_3));
	}
};

//! Bind a member function with automatic argument type deduction
template<typename char_t,typename F>
struct mem_function_binder
{
	//! Determine the number of parameters the function takes (including 'this')
	enum { N=boost::function_types::function_arity<F>::value };
	//! mem_function_binder_n type
	typedef typename mem_function_binder_n<char_t,F,N>::type type;
	//! Object type
	typedef typename mem_function_binder_n<char_t,F,N>::this_type this_type;
	//! Get a binder for an object instance and member function
	template<typename CLS>
	static type bind(CLS *c, F func)
	{
		return mem_function_binder_n<char_t,F,N>::bind(c,func);
	}
};

//! Join two binders together, firing them both when an option is encountered
template<typename char_t, typename C1,typename C2>
class bind_option_join_type : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Join binders together
	bind_option_join_type(
		const C1& a, //! First (primary) binder, used for argument counting and default string
		const C2& b  //! Second (auxilery) binder
		) : m_c1(a), m_c2(b) {}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		m_c1(opt,args);
		m_c2(opt,args);
	}
	
	std::string default_string() const 
	{
		return m_c1.default_string();
	}
	
	size_t num_args() const 
	{ 
		return m_c1.num_args(); 
	}
	
private:
	C1 m_c1;
	C2 m_c2;
};

//! Hide the default value displayed by a binder
template<typename char_t, typename CLS>
class hide_option_default_type : public basic::bind_option<char_t>
{
public:
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;

	//! Hide the default value displayed by a binder
	explicit hide_option_default_type(
		const CLS& c //!< The real binder to wrap
		) : m_c(c) {}
	
	void operator()(const string_type& opt,  const std::vector<string_type>& args )
	{
		m_c(opt,args);
	}
	
	std::string default_string() const 
	{
		return "";
	}
	
	size_t num_args() const 
	{ 
		return m_c.num_args(); 
	}
	
private:
	CLS m_c;
};

//! A structure used to define the type of a callback function as a member function of an object
template<typename char_t, typename CLS>
struct mem_callback
{
	//! String type
	typedef typename basic::bind_option<char_t>::string_type string_type;
	//! The callback type for a member of CLS
	typedef void (CLS::*type)(const string_type& opt,  const std::vector<string_type>& args);
};

} // namespace detail

namespace basic {

/*!
	\brief Bind an option to a variable
	
	Note that this requires nargs is 1 when bound to an option.
	
	Uses std::stringstream to convert to the variable type.
	
	\ingroup goptbind
*/
template<typename char_t, typename TYPE>
inline detail::bind_option_var_type1<char_t,TYPE> bind_option_var(TYPE& var)
{
	return detail::bind_option_var_type1<char_t,TYPE>(var);
}

/*!
	\brief Bind an option to a variable
	
	Note that this requires nargs is 2 when bound to an option.
	
	Uses std::stringstream to convert to the variable type.
	
	\ingroup goptbind
*/
template<typename char_t, typename TYPE1, typename TYPE2>
inline detail::bind_option_var_type2<char_t,TYPE1,TYPE2> bind_option_var(TYPE1& var1, TYPE2& var2)
{
	return detail::bind_option_var_type2<char_t,TYPE1,TYPE2>(var1,var2);
}

/*!
	\brief Bind an option to a variable
	
	Note that this requires nargs is 3 when bound to an option.
	
	Uses std::stringstream to convert to the variable type.
	
	\ingroup goptbind
*/
template<typename char_t, typename TYPE1, typename TYPE2, typename TYPE3>
inline detail::bind_option_var_type3<char_t,TYPE1,TYPE2,TYPE3> bind_option_var(TYPE1& var1, TYPE2& var2, TYPE3& var3)
{
	return detail::bind_option_var_type3<char_t,TYPE1,TYPE2,TYPE3>(var1,var2,var3);
}

/*!
	\brief Bind an option to a variable value
	
	If the option is present the variable will be set to the specified value
	
	\ingroup goptbind
*/
template<typename char_t, typename TYPE>
inline detail::bind_option_value_type<char_t,TYPE> bind_option_value(TYPE& var, const TYPE& val)
{
	return detail::bind_option_value_type<char_t,TYPE>(var,val);
}

/*!
	\brief Bind an option to a function pointer or functor
	
	f must implement
	operator()(const std::string& opt,  const std::vector<std::string>& args )
	
	Note that function objects are copied, use bind_option_mem_fun to call a 
	function on an object instance.
	
	\ingroup goptbind
*/
template<typename char_t, typename FUN>
inline detail::bind_option_callback_type<char_t> bind_option_callback(FUN f, size_t args)
{
	return detail::bind_option_callback_type<char_t>(f,args);
}

/*!
	\brief Bind an option to a member function pointer
	
	f must have the signature
	void operator()(const std::string& opt,  const std::vector<std::string>& args )
	
	\ingroup goptbind
*/
template<typename char_t, typename CLS>
inline typename detail::bind_option_callback_type<char_t> bind_option_mem_callback(
	CLS *c, 
	typename detail::mem_callback<char_t,CLS>::type f,
	size_t args)
{
	return bind_option_callback<char_t>(boost::bind(f,c,_1,_2),args);
}

/*!
	\brief Bind an option to a function pointer with automatic 
	parameter conversion.
	
	f can be any callable function, but not a callable object because automatic 
	parameter conversion does not work with function objects. To work with 
	function objects use bind_option_mem_fun.
	
	The function can take up to 3 arguments of any type, 
	the command line arguments are converted to the function argument types 
	using std::stringstream.
	
	The number of function arguments must match the number declared in the 
	option() call.
	
	\ingroup goptbind
*/
template<typename char_t, typename FUN>
inline typename detail::function_binder<char_t,FUN>::type bind_option_fun(FUN f)
{
	return typename detail::function_binder<char_t,FUN>::type(f);
}

/*!
	\brief Bind an option to a member function.
	
	c must be an object and f a member function of that object.
	
	The function (or function object) can take up to 3 arguments of any type, 
	the command line arguments are converted to the function argument types 
	using std::stringstream.
	
	The number of function arguments must match the number declared in the 
	option() call.
	
	\ingroup goptbind
*/
template<typename char_t, typename CLS, typename FUN>
inline typename detail::mem_function_binder<char_t,FUN>::type bind_option_mem_fun(CLS *c, FUN f)
{
	return detail::mem_function_binder<char_t,FUN>::bind(c,f);
}

/*!
	\brief Combine two option binders into a single option binder.
	
	The default value will be taken from the first binder, both binders will 
	be executed whenever the option is found.
	
	\ingroup goptbind
*/
template<typename char_t, typename C1,typename C2>
inline detail::bind_option_join_type<char_t,C1,C2> bind_option_join(const C1& a, const C2& b)
{
	return detail::bind_option_join_type<char_t,C1,C2>(a,b);
}

/*!
	\brief Hide the default value of an option binder 
	
	The binder will behave as normal, but no defailt value will be shown.
	
	\ingroup goptbind
*/
template<typename char_t, typename C>
inline detail::hide_option_default_type<char_t,C> hide_option_default(const C& a)
{
	return detail::hide_option_default_type<char_t,C>(a);
}
	
} // namespace basic

namespace wide {
	
/*! 
	\brief Base class for option binders working on std::wstrings
	\ingroup woptbind
*/
typedef basic::bind_option<wchar_t> bind_option;
	
/*! 
	\brief Ignore an option
	\ingroup woptbind
*/
typedef basic::bind_option_ignore<wchar_t> bind_option_ignore;
	
/*!
	\brief Bind an option to a variable
	
	Note that this requires nargs is 1 when bound to an option.
	
	Uses std::stringstream to convert to the variable type.
	
	\ingroup woptbind
*/
template<typename TYPE>
inline detail::bind_option_var_type1<wchar_t,TYPE> bind_option_var(TYPE& var)
{
	return detail::bind_option_var_type1<wchar_t,TYPE>(var);
}

/*!
	\brief Bind an option to a variable
	
	Note that this requires nargs is 2 when bound to an option.
	
	Uses std::stringstream to convert to the variable type.
	
	\ingroup woptbind
*/
template<typename TYPE1, typename TYPE2>
inline detail::bind_option_var_type2<wchar_t,TYPE1,TYPE2> bind_option_var(TYPE1& var1, TYPE2& var2)
{
	return detail::bind_option_var_type2<wchar_t,TYPE1,TYPE2>(var1,var2);
}

/*!
	\brief Bind an option to a variable
	
	Note that this requires nargs is 3 when bound to an option.
	
	Uses std::stringstream to convert to the variable type.
	
	\ingroup woptbind
*/
template<typename TYPE1, typename TYPE2, typename TYPE3>
inline detail::bind_option_var_type3<wchar_t,TYPE1,TYPE2,TYPE3> bind_option_var(TYPE1& var1, TYPE2& var2, TYPE3& var3)
{
	return detail::bind_option_var_type3<wchar_t,TYPE1,TYPE2,TYPE3>(var1,var2,var3);
}

/*!
	\brief Bind an option to a variable value
	
	If the option is present the variable will be set to the specified value
*/
template<typename TYPE>
inline detail::bind_option_value_type<wchar_t,TYPE> bind_option_value(TYPE& var, const TYPE& val)
{
	return detail::bind_option_value_type<wchar_t,TYPE>(var,val);
}

/*!
	\brief Bind an option to a function pointer or functor
	
	f must implement
	operator()(const std::string& opt,  const std::vector<std::string>& args )
	
	Note that function objects are copied, use bind_option_mem_fun to call a 
	function on an object instance.
	
	\ingroup woptbind
*/
template<typename FUN>
inline detail::bind_option_callback_type<wchar_t> bind_option_callback(FUN f, size_t args)
{
	return detail::bind_option_callback_type<wchar_t>(f,args);
}

/*!
	\brief Bind an option to a member function pointer
	
	f must have the signature
	void operator()(const std::string& opt,  const std::vector<std::string>& args )
	
*/
template<typename CLS>
inline typename detail::bind_option_callback_type<wchar_t> bind_option_mem_callback(
	CLS *c, 
	typename detail::mem_callback<wchar_t,CLS>::type f,
	size_t args)
{
	return bind_option_callback(boost::bind(f,c,_1,_2),args);
}

/*!
	\brief Bind an option to a function pointer with automatic 
	parameter conversion.
	
	f can be any callable function, but not a callable object because automatic 
	parameter conversion does not work with function objects. To work with 
	function objects use bind_option_mem_fun.
	
	The function can take up to 3 arguments of any type, 
	the command line arguments are converted to the function argument types 
	using std::stringstream.
	
	The number of function arguments must match the number declared in the 
	option() call.
	
	\ingroup woptbind
*/
template<typename FUN>
inline typename detail::function_binder<wchar_t,FUN>::type bind_option_fun(FUN f)
{
	return typename detail::function_binder<wchar_t,FUN>::type(f);
}

/*!
	\brief Bind an option to a member function.
	
	c must be an object and f a member function of that object.
	
	The function (or function object) can take up to 3 arguments of any type, 
	the command line arguments are converted to the function argument types 
	using std::stringstream.
	
	The number of function arguments must match the number declared in the 
	option() call.
	
	\ingroup woptbind
*/
template<typename CLS, typename FUN>
inline typename detail::mem_function_binder<wchar_t,FUN>::type bind_option_mem_fun(CLS *c, FUN f)
{
	return detail::mem_function_binder<wchar_t,FUN>::bind(c,f);
}

/*!
	\brief Combine two option binders into a single option binder.
	
	The default value will be taken from the first binder, both binders will 
	be executed whenever the option is found.
	
	\ingroup woptbind
*/
template<typename C1,typename C2>
inline detail::bind_option_join_type<wchar_t,C1,C2> bind_option_join(const C1& a, const C2& b)
{
	return detail::bind_option_join_type<wchar_t,C1,C2>(a,b);
}

/*!
	\brief Hide the default value of an option binder 
	
	The binder will behave as normal, but no defailt value will be shown.
	
	\ingroup woptbind
*/
template<typename C>
inline detail::hide_option_default_type<wchar_t,C> hide_option_default(const C& a)
{
	return detail::hide_option_default_type<wchar_t,C>(a);
}
	
} // namespace wide

/*! 
	\brief Base class for option binders working on std::strings
	\ingroup coptbind
*/
typedef basic::bind_option<char> bind_option;

/*! 
	\brief Ignore an option.
	\ingroup coptbind
*/
typedef basic::bind_option_ignore<char> bind_option_ignore;

/*!
	\brief Bind an option to a variable
	
	Note that this requires nargs is 1 when bound to an option.
	
	Uses std::stringstream to convert to the variable type.
	
	\ingroup coptbind
*/
template<typename TYPE>
inline detail::bind_option_var_type1<char,TYPE> bind_option_var(TYPE& var)
{
	return detail::bind_option_var_type1<char,TYPE>(var);
}

/*!
	\brief Bind an option to a variable
	
	Note that this requires nargs is 2 when bound to an option.
	
	Uses std::stringstream to convert to the variable type.
	
	\ingroup coptbind
*/
template<typename TYPE1, typename TYPE2>
inline detail::bind_option_var_type2<char,TYPE1,TYPE2> bind_option_var(TYPE1& var1, TYPE2& var2)
{
	return detail::bind_option_var_type2<char,TYPE1,TYPE2>(var1,var2);
}

/*!
	\brief Bind an option to a variable
	
	Note that this requires nargs is 3 when bound to an option.
	
	Uses std::stringstream to convert to the variable type.
	
	\ingroup coptbind
*/
template<typename TYPE1, typename TYPE2, typename TYPE3>
inline detail::bind_option_var_type3<char,TYPE1,TYPE2,TYPE3> bind_option_var(TYPE1& var1, TYPE2& var2, TYPE3& var3)
{
	return detail::bind_option_var_type3<char,TYPE1,TYPE2,TYPE3>(var1,var2,var3);
}

/*!
	\brief Bind an option to a variable value
	
	If the option is present the variable will be set to the specified value
	
	\ingroup coptbind
*/
template<typename TYPE>
inline detail::bind_option_value_type<char,TYPE> bind_option_value(TYPE& var, const TYPE& val)
{
	return detail::bind_option_value_type<char,TYPE>(var,val);
}

/*!
	\brief Bind an option to a function pointer or functor
	
	f must implement
	operator()(const std::string& opt,  const std::vector<std::string>& args )
	
	Note that function objects are copied, use bind_option_mem_fun to call a 
	function on an object instance.
	
	\ingroup coptbind
*/
template<typename FUN>
inline detail::bind_option_callback_type<char> bind_option_callback(FUN f, size_t args)
{
	return detail::bind_option_callback_type<char>(f,args);
}

/*!
	\brief Bind an option to a member function pointer
	
	f must have the signature
	void operator()(const std::string& opt,  const std::vector<std::string>& args )
	
	\ingroup coptbind
*/
template<typename CLS>
inline typename detail::bind_option_callback_type<char> bind_option_mem_callback(
	CLS *c, 
	typename detail::mem_callback<char,CLS>::type f,
	size_t args)
{
	return bind_option_callback(boost::bind(f,c,_1,_2),args);
}

/*!
	\brief Bind an option to a function pointer with automatic 
	parameter conversion.
	
	f can be any callable function, but not a callable object because automatic 
	parameter conversion does not work with function objects. To work with 
	function objects use bind_option_mem_fun.
	
	The function can take up to 3 arguments of any type, 
	the command line arguments are converted to the function argument types 
	using std::stringstream.
	
	The number of function arguments must match the number declared in the 
	option() call.
	
	\ingroup coptbind
*/
template<typename FUN>
inline typename detail::function_binder<char,FUN>::type bind_option_fun(FUN f)
{
	return typename detail::function_binder<char,FUN>::type(f);
}

/*!
	\brief Bind an option to a member function.
	
	c must be an object and f a member function of that object.
	
	The function (or function object) can take up to 3 arguments of any type, 
	the command line arguments are converted to the function argument types 
	using std::stringstream.
	
	The number of function arguments must match the number declared in the 
	option() call.
	
	\ingroup coptbind
*/
template<typename CLS, typename FUN>
inline typename detail::mem_function_binder<char,FUN>::type bind_option_mem_fun(CLS *c, FUN f)
{
	return detail::mem_function_binder<char,FUN>::bind(c,f);
}

/*!
	\brief Combine two option binders into a single option binder.
	
	The default value will be taken from the first binder, both binders will 
	be executed whenever the option is found.
	
	\ingroup coptbind
*/
template<typename C1,typename C2>
inline detail::bind_option_join_type<char,C1,C2> bind_option_join(const C1& a, const C2& b)
{
	return detail::bind_option_join_type<char,C1,C2>(a,b);
}

/*!
	\brief Hide the default value of an option binder 
	
	The binder will behave as normal, but no defailt value will be shown.
	
	\ingroup coptbind
*/
template<typename C>
inline detail::hide_option_default_type<char,C> hide_option_default(const C& a)
{
	return detail::hide_option_default_type<char,C>(a);
}

} // namespace woptions

#endif
